package edu.pitt.dbmi.odie.gapp.gwt.server.user;

import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.security.GeneralSecurityException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.gdata.client.docs.DocsService;
import com.google.gdata.client.http.AuthSubUtil;
import com.google.gdata.data.DateTime;
import com.google.gdata.data.docs.DocumentListEntry;
import com.google.gdata.data.docs.DocumentListFeed;
import com.google.gdata.data.extensions.LastModifiedBy;
import com.google.gdata.util.AuthenticationException;

@SuppressWarnings("serial")
public class ODIE_SignInServlet extends HttpServlet {

	/*
	 * There are two domains 1) standard - dictates the domain parameter be null
	 * for most calls 2) webodie.net - dictates the domain parameter be set to
	 * webodie.net
	 * 
	 * There are two mechanisms in play 1) UserServiceFactory, UserService, User
	 * provided by appengine 2) AuthSubUtil provided by gdata core
	 */

	private static final Logger log = Logger.getLogger(ODIE_SignInServlet.class
			.getName());

	public static final String DEFAULT_HOST = "docs.google.com";
	public static final String URL_FEED = "/feeds";
	public static final String URL_DOWNLOAD = "/download";
	public static final String URL_CATEGORY_EXPORT = "/Export";
	public static final String UMLS_LICENSE_NAME = "UMLS_LICENSE";
	public static final String URL_GROUP_FOLDERS = "/folders";
	public static final String URL_PATH = "/private/full";
	private static final String URL_DOCUMENTS_LIST = "/feeds/documents/private/full/-/document?prettyprint=true";
	private static final String GDOCS_DEF_FEED_HOST = "docs.google.com";
	private static final String GDOCS_FOLDERS_LIST = "/feeds/documents/private/full/-/folder?prettyprint=true";
	public static final String GDOCS_FEED_URL_PATH = "/feeds/documents/private/full";

	private DocsService docsService = null;

	private ODIE_ServerSideLoginInfo statusInfo = null;

	public void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws IOException {
		log.log(Level.INFO, "ODIE_SignInServlet - doGet");
		statusInfo = (ODIE_ServerSideLoginInfo) getServletContext()
				.getAttribute(ODIE_ServerSideLoginInfo.class.getName());
		if (statusInfo == null) {
			statusInfo = new ODIE_ServerSideLoginInfo();
			getServletContext().setAttribute(
					ODIE_ServerSideLoginInfo.class.getName(), statusInfo);
		}

		String action = req.getParameter("action");
		if (action == null) {
			processStatus(req, resp);
		} else if (action.equals("status")) {
			processStatus(req, resp);
		} else if (action.equals("loginGoogle")) {
			processLoginGoogle(req, resp);
		} else if (action.equals("loginWebOdie") && getInitParameter("password") != null) {
			processLocal(req, resp);
		} else if (action.equals("loginWebOdie")) {
			processLoginWebOdie(req, resp);
		} else if (action.equals("logout")) {
			processLogout(req, resp);
		} else if (action.equals("revoke")) {
			processRevoke(req, resp);
		}
	}

	private void processLocal(HttpServletRequest req, HttpServletResponse resp) throws IOException {
		log.info("ODIE_SignInServlet - processLocal");
		UserService userService = UserServiceFactory.getUserService();
		statusInfo.setEmailAddress("kevin.jude.mitchell@gmail.com");
		statusInfo.setNickName("Kevin");
		statusInfo.setPassword(getInitParameter("password"));
		ODIE_UserUtils.establishSessionCache(this, statusInfo) ;
		this.getServletContext().setAttribute(statusInfo.getClass().getName(), statusInfo) ;
		String authzViewerUrl = assembleRequestUri(req) + "/ODIE_Client.html";
		resp.sendRedirect(authzViewerUrl);
	}

	private void processLoginGoogle(HttpServletRequest req,
			HttpServletResponse resp) throws IOException {
		log.info("ODIE_SignInServlet - processLoginGoogle");
		UserService userService = UserServiceFactory.getUserService();
		String nextUrl = assembleRequestUri(req)
				+ "/odie_client/signin?action=status";
		String loginUrl = userService.createLoginURL(nextUrl);
		resp.sendRedirect(loginUrl);
	}

	private void processLoginWebOdie(HttpServletRequest req,
			HttpServletResponse resp) throws IOException {
		log.info("ODIE_SignInServlet - processLoginWebOdie");
		UserService userService = UserServiceFactory.getUserService();
		String protocol = "http" ;
		String domain = "www.google.com" ;
		String hostedDomain = "webodie.net" ;
		String scope = "http://docs.google.com/feeds/";
		boolean secure = false;
		boolean session = true;
		String nextUrl = assembleRequestUri(req)
				+ "/odie_client/signin?action=status";
		log.info("nextUrl ==> " + nextUrl) ;
		String authSubUrl = AuthSubUtil.getRequestUrl(protocol, domain, hostedDomain, nextUrl,
				scope, secure, session);
		log.info("authSubUrl ==> " + URLDecoder.decode(authSubUrl, "UTF-8")) ;
		String loginUrl = userService.createLoginURL(authSubUrl, "webodie.net");
		log.info("loginUrl ==> " + URLDecoder.decode(loginUrl, "UTF-8")) ;
		resp.sendRedirect(loginUrl);
	}

	private void processLogout(HttpServletRequest req, HttpServletResponse resp) {
		// TODO Auto-generated method stub
	}

	private void processRevoke(HttpServletRequest req, HttpServletResponse resp) {
		// TODO Auto-generated method stub

	}

	private void processStatus(HttpServletRequest req, HttpServletResponse resp)
			throws IOException {
		log.info("ODIE_SignInServlet - processStatus");
		UserService userService = UserServiceFactory.getUserService();
		statusInfo.setUser(userService.getCurrentUser());
		String authSubToken = AuthSubUtil.getTokenFromReply(req
				.getQueryString());
		String authSubSessionToken = exchangeForAuthSubSessionToken(
				authSubToken, null);
		statusInfo.setHostedDomain("webodie.net") ;
		statusInfo.setAuthSubToken(authSubToken);
		statusInfo.setAuthSubSessionToken(authSubSessionToken);
		boolean testDocsServiceResult = testDocsService(statusInfo) ;
		log.info("testDocsServiceResult is " + testDocsServiceResult) ;
		statusInfo.setReadingDocuments(testDocsServiceResult);
//		String authzViewerUrl = assembleRequestUri(req) + "/webodie_authz.jsp";
		ODIE_UserUtils.establishSessionCache(this, statusInfo) ;
		this.getServletContext().setAttribute(statusInfo.getClass().getName(), statusInfo) ;
		String authzViewerUrl = assembleRequestUri(req) + "/ODIE_Client.html";
		resp.sendRedirect(authzViewerUrl);
	}

	private String exchangeForAuthSubSessionToken(String authSubToken,
			String domain) {
		String authSubSessionToken = null;
		try {
			if (authSubToken != null) {
				if (domain != null) {
					authSubSessionToken = AuthSubUtil.exchangeForSessionToken(
							"http", domain, authSubToken, null);
				} else {
					authSubSessionToken = AuthSubUtil.exchangeForSessionToken(
							authSubToken, null);
				}
			}
		} catch (AuthenticationException e) {
			;
		} catch (IOException e) {
			;
		} catch (GeneralSecurityException e) {
			;
		}
		return authSubSessionToken;
	}

	private boolean testDocsService(ODIE_ServerSideLoginInfo serverSideLoginInfo) {
		boolean result = false;
		try {
			DocsService docsService = new DocsService("webodie_authz");
			// docsService.setAuthSubToken(serverSideLoginInfo
			// .getAuthSubSessionToken(), null);
			String emailAddress = serverSideLoginInfo.getEmailAddress();
			String password = serverSideLoginInfo.getPassword();
			String authSubSessionToken = serverSideLoginInfo.getAuthSubSessionToken() ;
			String authSubToken = serverSideLoginInfo.getAuthSubToken() ;
			if (password != null) {
				docsService.setUserCredentials(emailAddress, password);
			}
			else if (authSubSessionToken != null) {
				docsService.setAuthSubToken(authSubSessionToken, null /* private key */);
			} 
			else if (authSubToken != null) {
				docsService.setAuthSubToken(authSubToken, null /* private key */);
			}
			
			DocumentListFeed feed;
			URL documentListFeedUrl = new URL("http://" + GDOCS_DEF_FEED_HOST
					+ "/feeds/documents/private/full/-/folder?prettyprint=true");
			documentListFeedUrl = new URL(
					"http://docs.google.com/feeds/default/private/full");
			// These System Properties turn off a warning message from within
			// the GData documents service
			System.setProperty("org.apache.commons.logging.Log",
					"org.apache.commons.logging.impl.SimpleLog");
			System
					.setProperty(
							"org.apache.commons.logging.simplelog.showdatetime",
							"true");
			System
					.setProperty(
							"org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient",
							"error");
			feed = docsService.getFeed(documentListFeedUrl,
					DocumentListFeed.class);
			List<DocumentListEntry> entries = feed.getEntries();
			if (entries != null && !entries.isEmpty()) {
				result = true;
				statusInfo.setDocumentEntryText(printDocumentEntry(entries.get(0))) ;
			}

		} catch (AuthenticationException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	public String printDocumentEntry(DocumentListEntry doc) {
		StringBuffer sb = new StringBuffer() ;
		String resourceId = doc.getResourceId();
		String docType = resourceId.substring(0, resourceId.lastIndexOf(':'));
		
		sb.append("'" + doc.getTitle().getPlainText() + "' ("
				+ docType + ")");
		sb.append("  link to Google Docs: "
				+ doc.getHtmlLink().getHref());
		sb.append("  resource id: " + resourceId);

		// print the timestamp the document was last viewed
		DateTime lastViewed = doc.getLastViewed();
		if (lastViewed != null) {
			sb.append("  last viewed: " + lastViewed.toString());
		}

		// print who made that modification
		LastModifiedBy lastModifiedBy = doc.getLastModifiedBy();
		if (lastModifiedBy != null) {
			sb.append("  updated by: " + lastModifiedBy.getName()
					+ " - " + lastModifiedBy.getEmail());
		}

		// print other useful metadata
		sb.append("  last updated: " + doc.getUpdated().toString());
		sb.append("  viewed by user? " + doc.isViewed());
		sb.append("  writersCanInvite? "
				+ doc.isWritersCanInvite().toString());
		sb.append("  hidden? " + doc.isHidden());
		sb.append("  starred? " + doc.isStarred());
		return sb.toString() ;
	}
	
	public static String assembleRequestUri(HttpServletRequest req) {
		String servletPath = req.getServletPath();
		StringBuffer requestURLBuffer = req.getRequestURL();
		String requestURL = (requestURLBuffer.length() > 0) ? requestURLBuffer
				.toString() : null;
		requestURL = requestURL.substring(0, requestURL
				.lastIndexOf(servletPath));
		return requestURL;
	}

}
